package com.springchang.easyelasticsearch.core;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.springchang.easyelasticsearch.annotation.EsIndex;
import com.springchang.easyelasticsearch.exception.IllegalFieldException;
import com.springchang.easyelasticsearch.exception.IndexExistException;
import com.springchang.easyelasticsearch.valuable.Paramater;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.*;

/**
 * @author : 张翠山
 */
public class RestHighLevelClientPlus extends RestHighLevelClientAbs {

    public  <T> List<T> getList(Selector selector, Class clazz) throws IllegalArgumentException, ElasticsearchStatusException, IOException {
        List<Map<String, Object>> list = this.getList(selector);

        if(CollectionUtils.isEmpty(list)) {
            return Collections.EMPTY_LIST;
        }
        List<T> newList = new ArrayList<T>(list.size());
        for (Map<String, Object> data : list) {
            try {
                T t = (T) JSON.parseObject(JSON.toJSONString(data), clazz);
                newList.add(t);
            } catch (JSONException jsonException) {
                log.error("JSON转换失败: {}", jsonException.getMessage());
            }
        }
        return newList;
    }

    public List<Map<String, Object>> getList(Selector selector) throws IllegalArgumentException, ElasticsearchStatusException, IOException {
        return query(selector);
    }

    public Map<String, Object> getOne(Selector selector) throws IllegalArgumentException, ElasticsearchStatusException, IOException {
        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();

        try {
            list = query(selector);
        } catch (ElasticsearchStatusException elasticsearchStatusException) {
            throw elasticsearchStatusException;
        } catch (IOException ioException) {
            throw ioException;
        } catch (Exception exception) {
            throw exception;
        }

        if(CollectionUtils.isEmpty(list)) {
            return Collections.EMPTY_MAP;
        }
        return list.get(0);
    }

    public  <T> T getOne(Selector queryWrapper, Class clazz) throws IllegalArgumentException, ElasticsearchStatusException, IOException {
        if(queryWrapper == null || queryWrapper.getIndex() == null || "".equals(queryWrapper.getIndex())) {
            throw new IllegalArgumentException("查询包装类为空或索引为空");
        }
        if(clazz == null) {
            throw new IllegalArgumentException("返回值类型未定义");
        }
        List<T> list = this.getList(queryWrapper, clazz);
        if(CollectionUtils.isEmpty(list)) {
            return null;
        }
        return list.get(0);
    }

    /**
     * @param entity
     * @return
     */
    public <T> boolean create(T entity) throws NullPointerException, IllegalArgumentException, IllegalAccessException {

        Paramater paramater = super.parseGeneric(entity);

        addData(paramater.getIndex(), JSON.toJSONString(entity));

        return true;
    }

    public boolean create(String indexName, Map<String, Object> data) {
        addData(indexName, JSON.toJSONString(data));
        return true;
    }

    public <T> boolean update(T entity) throws NullPointerException, IllegalArgumentException, IllegalAccessException, IOException {
        Paramater paramater = super.parseGeneric(entity);

        if(paramater.isEmpty()) {
            throw new NullPointerException("实体解析错误,缺少框架所需要的参数");
        }
        super.update(paramater.getIndex(), paramater.getIdName(), paramater.getIdValue(), entity);

        return true;
    }

    public boolean update(String index, String idName, Object idValue, Map<String, Object> data) throws NullPointerException, IOException {
        if (StringUtils.isEmpty(index)) {
            throw new NullPointerException("索引[" + index + "]为空");
        }
        if (StringUtils.isEmpty(idName)) {
            throw new NullPointerException("主键[" + index + "]为空");
        }

        SearchRequest searchRequest = new SearchRequest(index);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.must(QueryBuilders.termQuery("deleted", false));
        boolQueryBuilder.must(QueryBuilders.termQuery(idName, idValue));
        searchSourceBuilder.query(boolQueryBuilder);
        searchSourceBuilder.size(2);
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHit[] searchHits = searchResponse.getHits().getHits();
        for (SearchHit s : searchHits) {
            String docId = s.getId();
            UpdateRequest request = new UpdateRequest(index, "_doc", docId);
            request.doc(JSON.toJSONString(data), XContentType.JSON);
            request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
            UpdateResponse resp = restHighLevelClient.update(request, RequestOptions.DEFAULT);
        }

        return true;
    }

    public <T> boolean logicDelete(T entity) throws IllegalArgumentException, IllegalAccessException, IOException {

        Paramater paramater = super.parseGeneric(entity);

        if(paramater.getIdValue() == null) {
            throw new IllegalArgumentException("要删除的对象组件为空");
        }

        super.logicDelete(paramater.getIndex(), paramater.getIdName(), paramater.getIdValue());
        return true;
    }

    /**
     * 根据条件删除记录数
     * @param index 索引
     * @param boolQueryBuilder 查询条件
     * @return 成功删除记录数
     * @throws IOException
     */
    public long deleteByQuery(String index, BoolQueryBuilder boolQueryBuilder) throws IOException {
        DeleteByQueryRequest request = new DeleteByQueryRequest(null);
//        request.setQuery(boolQueryBuilder);
        request.setRefresh(true);
        BulkByScrollResponse resp = restHighLevelClient.deleteByQuery(request, RequestOptions.DEFAULT);
        return resp.getStatus().getDeleted();
    }

    public BulkByScrollResponse updateByQuery(String index, QueryBuilder query, Map<String, Object> document) throws IOException {
        UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest(null);
//        updateByQueryRequest.setQuery(query);
        StringBuilder script = new StringBuilder();
        Set<String> keys = document.keySet();
        for (String key : keys) {
            String appendValue = "";
            Object value = document.get(key);
            if (value instanceof Number) {
                appendValue = value.toString();
            } else if (value instanceof String) {
                appendValue = "'" + value.toString() + "'";
            } else if (value instanceof List){
                appendValue = JSON.toJSONString(value);
            } else {
                appendValue = value.toString();
            }
            script.append("ctx._source.").append(key).append("=").append(appendValue).append(";");
        }
        updateByQueryRequest.setScript(new Script(script.toString()));
        return restHighLevelClient.updateByQuery(updateByQueryRequest, RequestOptions.DEFAULT);
    }

    /**
     * 判断索引是否存在
     *
     * @param index
     * @return
     * @throws IOException
     */
    public boolean isIndexExist(String index) throws IOException {
        return super.isIndexExist(index);
    }

    public boolean creasteIndex(Class clazz) throws IndexExistException, IllegalFieldException, IOException {

        log.info(clazz.toString());
        //通过反射获取@Id注解下的table名字
        Annotation[] annotations = clazz.getAnnotations();

        EsIndex tableAnnotation = null;
        for (Annotation annotation : annotations) {
            if (annotation instanceof EsIndex) {
                tableAnnotation = (EsIndex) annotation;
                break;
            }
        }

        String indexName = tableAnnotation.name();

        if (tableAnnotation == null) {
            throw new IllegalArgumentException(clazz.getSimpleName() + "类定义缺少@Table注解");
        }
        if (StringUtils.isEmpty(tableAnnotation.name())) {
            throw new IllegalArgumentException(clazz.getSimpleName() + "类定义@Table注解name()属性值为空");
        }
        if (this.isIndexExist(indexName)) {
            throw new IndexExistException("索引[" + indexName + "]已经存在!");
        }

        return super.creasteIndex(indexName, clazz);
    }

    public boolean creasteIndex(String indexName, Class clazz) throws IndexExistException, IOException {
        if (this.isIndexExist(indexName)) {
            throw new IndexExistException("索引[" + indexName + "]已经存在!");
        }

        return super.creasteIndex(indexName, clazz);
    }
}